"use strict";

var radon = require('radon');
var Int64 = radon.Int64;
var util = radon.util;
var generator = util.generator;
var Mongo_INSTANCE = {};	// 以服务器为键名的链接池.
var URI_REGX = /^(?:([^:]+):\/\/)?((?:[^\/]+\/)?(.+))$/;
var DB = require('mongodb').MongoClient;
var dbConf = radon.config('db');

var db_option = {
	server: {
		// 链接池
		poolSize: dbConf.max_queue || 50	//链接池的最大数目
	}
	,promiseLibrary: util.getPromiseClass()
};

var connect = generator(function*(server) {
	//取一个实例;
	var db;
	if(Mongo_INSTANCE[server])
	{
		db = Mongo_INSTANCE[server];
		try
		{
			var stat = yield db.stats();
			if(stat && stat.ok == 1)
			{
				return db;
			}
		}
		catch(e)
		{
			util.log(54, e && e.stack || e);
		}

		util.log(54, `The Db Server ${server} is Dead, reconnect`);
		Mongo_INSTANCE[server] = null;
	}

	var cfg = dbConf.servers[server];
	if (cfg && cfg.type == 'MongoDB')
	{
		cfg = cfg.uri;
		if (cfg.indexOf('mongodb://') !== 0)
		{
			cfg = 'mongodb://' + cfg;
		}

		db = yield DB.connect(cfg, db_option);
		Mongo_INSTANCE[server] = db;
		db.on('error', function(e) {
			util.log(54, 'mongo Error   ' + e);
			if(Mongo_INSTANCE[server] && Mongo_INSTANCE[server].close) {
				Mongo_INSTANCE[server].close();
			}
			Mongo_INSTANCE[server] = null;
		});

		db.on('close', function(e) {
			util.log(54, 'mongo close  ' + e);
			Mongo_INSTANCE[server] = null;
		});

		return db;
	}
	else
	{
		var err = new Error('ModelDB no server setting');
		err.code = 7000;
		throw err;
	}
});

exports.getDb = connect;


var getMongoInstance = generator(function*(coll) {
	var dbConf = radon.config('db');
	var models = dbConf.models;

	var match = URI_REGX.exec(coll);
	var cfg = models[match[2]] || models[match[3]] || null;

	var server = match[1] || (cfg && cfg.server) || dbConf.default_server;
	coll = (cfg && cfg.collection) || match[3];

	var db = yield connect(server);
	return new MongoInstance(server, coll, db, db.collection(coll));
});

exports.getMongoInstance = getMongoInstance;

function MongoInstance(server,collection, db, coll) {
	var self = this;
	self.serverName = server;
	self.db = db;
	self.collection = collection;
	self.coll = coll;
}

MongoInstance.prototype.find = function(query) {
	var self = this;
	return self.coll.find(query);
};

MongoInstance.prototype.reConnect = function() {
};

/*
 释放mongo实例
 */
MongoInstance.prototype.free = function() {
	this.db = null;
	this.collection = null;
	return this;
};

MongoInstance.prototype.close_server = function(){
	var server = Mongo_INSTANCE[this.serverName];
	if(server && server.close)
	{
		server.close();
	}

	Mongo_INSTANCE[this.serverName] = null;
};

MongoInstance.prototype.runCommand = generator(function*(param) {
	try {
		var ret = yield this.coll.aggregate(param, {cursor: {batchSize: 1}});
		this.reconnect = 0;
	}
	catch(e) {
		this.reConnect();
		return yield this.runCommand.apply(this, arguments);
	}

	let result = yield ret.toArray();
	if(ret && ret.close) {
		ret.close();
	}

	return result;
});

MongoInstance.prototype.update = function(selector, documents, options){
	return this.coll.update(selector, documents, options);
};

// 普通Mongo查询
function mongoFind(query, where){
	var self = this;
	self._conf = query;
	self.model = query.model;
	self.query = where.query || {};
	self.fields = {};
	self.sort = where.sort;
	self.course = null;

	if(where.fields) {
		where.fields.forEach(function(field){
			self.fields[field] = 1;
		});
	}
}

mongoFind.prototype.where = function(query) {
	var self = this;
	self.query = query;
	if (self.course){
		if(self.course.close) {
			self.course.close();
		}
		self.course = null;
	}
	return self;
};

mongoFind.prototype.getField = function*(field, param) {
	let self = this;
	if(!self.db) {
		self.db = yield getMongoInstance(self.model);
	}

	let pipe = [];
	if (self.query){
		pipe.push({'$match': self.query});
	}

	let groupField = self._conf.groupField || "CreateUserId";
	pipe.push({$group: {_id: `$${groupField}`, "Fields": {"$push": `$${field}`}}});
	if (param){
		if (param['offset'] > 0){
			pipe.push({'$skip': param['offset']});
		}
		if (param['size'] > 0){
			pipe.push({'$limit': param['size']});
		}
	}

	let data = yield self.db.runCommand(pipe);
	let ids = [];
	data.reduce(function (s, v) {
		[].push.apply(s, v.Fields);
		return s;
	}, ids);

	return ids;
};

mongoFind.prototype.getResult = generator(function*(field, param) {
	var self = this;
	if(!self.course) {
		self.course = yield self.doFind();
	}

	//过滤查询字段
	let _project = self.fields;
	if(field) {
		_project = {[field]: 1};
	}
	self.course.project(_project);

	if (self.sort){
		self.course.sort(self.sort);
	}
	if (param){
		if (param.offset > 0){
			self.course.skip(param.offset);
		}
		if (param.size > 0){
			self.course.limit(param.size);
		}
	}

	var result = yield self.course.toArray();
	var items = result;
	if (field){
		items = [];
		result.forEach(function(item) {
			items.push(item[field]);
		});
	}

	if(self.course.close) {
		self.course.close();
	}
	self.course = null;
	return items;
});

mongoFind.prototype.count = generator(function*() {
	var self = this;
	if (!self.course){
		self.course = yield self.doFind();
	}

	return yield self.course.count();
});

mongoFind.prototype.doFind = generator(function*() {
	let self = this;
	if(!self.db) {
		self.db = yield getMongoInstance(self.model);
	}

	let cursor = yield self.db.find(self.query);
	if(cursor.isClosed())
	{
		util.log(54, 'the Cursor is closed');
		self.db.close_server();
		radon.throw(10000008);
	}

	return cursor;
});

mongoFind.prototype.free = function() {
	if(this.db) {
		this.db.free();
	}
	this.db = null;
};

exports.mongoFind = mongoFind;

exports.mongoAggregate = mongoAggregate;

// Mongo 聚合查询框架
function mongoAggregate(query, where, pipe){
	var self = this;
	self.model = query.model;
	self.pipe = pipe;
	self.query = checkEmpty(where.query);
	self.sort = where.sort;
}

mongoAggregate.prototype.where = function(query) {
	this.query = checkEmpty(query);
	return this;
};

mongoAggregate.prototype.getResult = generator(function*(field, param) {
	var self = this;
	var cmds = [];
	if (self.sort){
		cmds.push({'$sort': self.sort});
	}
	if (param){
		if (param['offset'] > 0){
			cmds.push({'$skip': param['offset']});
		}
		if (param['size'] > 0){
			cmds.push({'$limit': param['size']});
		}
	}

	var res = yield self.doRun(cmds);
	res = (res ? res : []);
	var items = res;

	if (field){
		items = [];
		res.forEach(function(item){
			item = item[field];
			//items.push((item instanceof Int64) ? item.toNumber() : item);
			items.push(item);
		});
	}

	return items;
});

mongoAggregate.prototype.count = generator(function*() {
	var self = this;
	var res = yield self.doRun([{'$group':{'_id':null, 'c':{'$sum':1}}}]);
	return (res && res.length && res[0]['c'] || 0);
});

mongoAggregate.prototype.doRun = generator(function*(cmds) {
	var self = this;
	var pipe = self.pipe.slice();
	if (self.query){
		pipe.push({'$match': self.query});
	}
	if (cmds && cmds.length > 0){
		pipe.push.apply(pipe, cmds);
	}

	if(!self.db) {
		self.db = yield getMongoInstance(self.model);
	}

	return yield self.db.runCommand(pipe);
});

mongoAggregate.prototype.free = function() {
	if(this.db) {
		this.db.free();
	}
	this.db = null;
};


function checkEmpty(obj){
	if (typeof obj == 'object' && util.isEmpty(obj)){
		return null;
	}else {
		return obj;
	}
}


/**
 *更新数据
 * @param collection	表
 * @param selector		过滤条件
 * @param document		更新字段信息
 */
var mongoUpdate = function*(collection, selector, document){
	if (!collection || !selector || !document)
	{
		return;
	}
	//获取实例
	var db = yield getMongoInstance(collection);
	yield db.update(
		selector,
		//默认用set方法保存
		{'$set': document},
		//默认更新所有
		{'multi': true}
	);

	//释放数据实例
	if (db)
	{
		db.free();
	}
};

exports.mongoUpdate = mongoUpdate;

var mongoInc = function*(collection, selector, document){
	if (!collection || !selector || !document)
	{
		return;
	}
	//获取实例
	var db = yield getMongoInstance(collection);
	yield db.update(
		selector,
		//默认用set方法保存
		{'$inc': document},
		//默认更新所有
		{'multi': true}
	);

	//释放数据实例
	if (db)
	{
		db.free();
	}
};
exports.mongoInc = mongoInc;
